﻿/*
 * File Name:               Rectangle.cs
 * 
 * Description:             矩形
 * Author:                  zhangke <kris@zhanng.org>

 * Create Date:             2017/08/24
 */

using UnityEngine;

/// <summary>
/// 矩形构造
/// b|----start----| a               
///  |             | |               
///  |             | dis_s2e         
///  |             | |               
/// d|-----end-----| c               
/// 左右各 offset 宽
/// </summary>
public struct Rectangle
{
    public XVector2 start;
    public XVector2 end;
    public XNumber offset;
    public XNumber dis_s2e; // start - end 长度

    public XVector2 horizontal; // 自身坐标系 X 轴
    public XVector2 vertical; // 自身坐标系 Y 轴


    public Rectangle(XVector2 start, XVector2 end, XNumber offset)
    {
        this.start = start;
        this.end = end;
        this.offset = offset;

        dis_s2e = (start - end).magnitude;
        vertical = (end - start).normalized;
        horizontal = (XQuaternion.AngleAxis(90, XVector3.up) * vertical.x0z).xz;
    }

    /// <summary>
    // point4|----start----| point3
    //       |             | |
    //       |             | dis_s2e
    //       |             | |
    // point1|-----end-----| point2
    /// </summary>
    public Rectangle(XVector2 point1, XVector2 point2, XVector2 point3, XVector2 point4)
    {
        start = (point3 + point4) / 2;
        end = (point1 + point2) / 2;
        offset = (point1 - point2).magnitude / 2;

        dis_s2e = (start - end).magnitude;
        vertical = (end - start).normalized;
        horizontal = (point1 - point2).normalized;
    }

    public bool CheckIn(XVector2 point)
    {
        var s2p = point - start;
        var offsetX = XVector2.Dot(s2p, horizontal);
        if (offsetX < offset && offsetX > -offset)
        {
            var offsetY = XVector2.Dot(s2p, vertical);
            if (offsetY < dis_s2e && offsetY > 0)
            {
                return true;
            }
        }

        return false;
    }

    public void GetCorners(out XVector2 a, out XVector2 b, out XVector2 c, out XVector2 d)
    {
        a = start + horizontal * offset;
        b = start + horizontal * -offset;
        c = a + vertical * dis_s2e;
        d = b + vertical * dis_s2e;
    }

    /// <summary>
    /// 获取射线与矩形的交点以及在start-end上的投影点
    /// </summary>
    /// <param name="point">射线起点</param>
    /// <param name="dir">射线方向</param>
    /// <param name="c1">交点1</param>
    /// <param name="c2">交点2</param>
    /// <param name="v1">投影点1</param>
    /// <param name="v2">投影点2</param>
    public void GetCrossPoint(XVector2 point, XVector2 dir, out XVector2? c1, out XVector2? c2, ref XVector2 v1, ref XVector2 v2)
    {
        c1 = null;
        c2 = null;

        if (CheckIn(point))
        {
            c1 = point;
        }
        else if (dir.x * vertical.y - dir.y * vertical.x == 0 || dir.x * horizontal.y - dir.y * horizontal.x == 0)
        {
            // 与轴平行且不再矩形内 没有交点
            return;
        }

        XVector2 a, b, c, d;
        GetCorners(out a, out b, out c, out d);

        var c_ab = Math2D.Point_Cross_SengmentAndLine(a, b, point, dir);
        if (c_ab != null)
        {
            var p = c_ab.Value;
            if (XVector2.Dot(p - point, dir) >= 0)
            {
                if (c1 == null) c1 = c_ab;
                else c2 = c_ab;
            }
        }

        if (c1 == null || c2 == null)
        {
            var c_ac = Math2D.Point_Cross_SengmentAndLine(a, c, point, dir);
            if (c_ac != null)
            {
                var p = c_ac.Value;
                if (XVector2.Dot(p - point, dir) >= 0)
                {
                    if (c1 == null) c1 = c_ac;
                    else c2 = c_ac;
                }
            }
        }

        if (c1 == null || c2 == null)
        {
            var c_bd = Math2D.Point_Cross_SengmentAndLine(b, d, point, dir);
            if (c_bd != null)
            {
                var p = c_bd.Value;
                if (XVector2.Dot(p - point, dir) >= 0)
                {
                    if (c1 == null) c1 = c_bd;
                    else c2 = c_bd;
                }
            }
        }

        if (c1 == null || c2 == null)
        {
            var c_dc = Math2D.Point_Cross_SengmentAndLine(d, c, point, dir);
            if (c_dc != null)
            {
                var p = c_dc.Value;
                if (XVector2.Dot(p - point, dir) >= 0)
                {
                    if (c1 == null) c1 = c_dc;
                    else c2 = c_dc;
                }
            }
        }

        if (c1 == null || c2 == null)
        {
            // 没有交点
            return;
        }

        // 求垂点
        v1 = XVector2.Dot((c1.Value - start), vertical) * vertical + start;
        v2 = XVector2.Dot((c2.Value - start), vertical) * vertical + start;
    }

    /// <summary>
    /// 线段与矩形的交点
    /// </summary>
    /// <param name="pos01"></param>
    /// <param name="pos02"></param>
    /// <returns></returns>
    public XVector2? getCrossPoint(XVector2 pos01, XVector2 pos02)
    {
        // 这里为了简化计算量 仅计算了交叉线的交点 也就是结果并不处于矩形的边缘 但是应该会符合设计需求
        XVector2 a, b, c, d;
        GetCorners(out a, out b, out c, out d);
        var cross01 = Math2D.Point_Cross_SegmentAndSegment(a, c, pos01, pos02);
        if (cross01.HasValue)
            return cross01.Value;

        var cross02 = Math2D.Point_Cross_SegmentAndSegment(b, d, pos01, pos02);
        if (cross02.HasValue)
            return cross02.Value;

        return null;
    }

    /// <summary>
    /// 返回离该点最近的端点
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public XVector2 getNearestPoint(XVector2 pos)
    {
        XVector2 a, b, c, d;
        GetCorners(out a, out b, out c, out d);

        var disA = XVector2.DistanceSquare(a, pos);
        var disB = XVector2.DistanceSquare(b, pos);
        var disC = XVector2.DistanceSquare(c, pos);
        var disD = XVector2.DistanceSquare(d, pos);
        var curDis = disA;
        var curRet = a;
        if(disB < curDis)
        {
            curDis = disB;
            curRet = b;
        }

        if (disC < curDis)
        {
            curDis = disC;
            curRet = c;
        }

        if (disD < curDis)
        {
            curDis = disD;
            curRet = d;
        }

        return curRet;
    }

    public void Draw(XVector2 point)
    {
#if UNITY_EDITOR
        XVector2 a, b, c, d;
        GetCorners(out a, out b, out c, out d);

        Debug.DrawLine(a.x0z, b.x0z, Color.white);
        Debug.DrawLine(a.x0z, c.x0z, Color.white);
        Debug.DrawLine(b.x0z, d.x0z, Color.white);
        Debug.DrawLine(c.x0z, d.x0z, Color.white);

        if (point != null)
        {
            if (CheckIn(point))
            {
                Debug.DrawLine(start.x0z, point.x0z, Color.green);
                //Debug.DrawLine(end.x0z, point.x0z, Color.green);
            }
            else
            {
                Debug.DrawLine(start.x0z, point.x0z, Color.red);
                //Debug.DrawLine(end.x0z, point.x0z, Color.red);
            }
        }
#endif
    }
}